/**
 * 
 */
package com.testing.json;

import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;

import net.javacrumbs.json2xml.JsonXmlReader;
import net.sf.json.JSONObject;

import org.xml.sax.InputSource;

/**
 *	just a class demonstrating how to change objects into JSON PLUS how
 *	to change the JSON into XML
 * @author Jason
 * created at: Nov 2, 2011
 */
public class JSONToXMLHelper {
	
	/**
	 * method to transform a given Object to a JSONObject; simply passing 
	 * all the raw values to the JSONObject; might need to cater for Collection
	 * types later on.
	 * @param pObj
	 * @return
	 */
	public JSONObject transformObjectToJSON(final Object pObj) {
		Method[] aGetMethods=null;
		JSONObject aJObj=null;
		JSONObject aRoot=null;
		
		if (pObj!=null) {
			aJObj=new JSONObject();
			aRoot=new JSONObject();
			/*
			 * by reflection; extract all getXXX method with no parameters
			 */
			aGetMethods=this.getSimpleAccessorsFromObject(pObj);
			for (Method aMtd:aGetMethods) {
				try {
					Object aValue=aMtd.invoke(pObj);
					/*
					 * set it back to the new JSONObject
					 */
					aJObj.put(this.getMemberNameFromAccessorMethod(aMtd.getName()), aValue);
					
				} catch (Exception e) {
					e.printStackTrace();
				}
			}	// end for (aGetMethods)
			aRoot.put(pObj.getClass().getSimpleName(), aJObj);
		}
		return aRoot;
	}
	
	/**
	 *  method to transform a given Object to a JSON-String.
	 * @see #transformObjectToJSON(Object)
	 * @param pObj
	 * @return
	 */
	public String transformObjectToJSONString(final Object pObj) {
		JSONObject aJObj=this.transformObjectToJSON(pObj);
		String aVal=null;
		
		if (aJObj!=null) {
			aVal=aJObj.toString();
		}
		return aVal;
	}
	
	/**
	 * get the accessor methods of the given pObj; assume the pObj is 
	 * not null
	 * <p/>
	 * PS. this method should be a utility method based on "Reflection"
	 * @param pObj
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	private Method[] getSimpleAccessorsFromObject(final Object pObj) {
		Method[] aMethods=null;
		Method[] aFinalMtdsArr=null;
		List<Method> aFinalMtds=new ArrayList<Method>();
		
		if (pObj!=null) {
			aMethods=pObj.getClass().getDeclaredMethods();
			
			for (Method aMtd:aMethods) {
				/*
				 * criteria:
				 * 1) starts with "get"
				 * 2) no parameters
				 */
				if (aMtd.getName().startsWith("get")) {
					Class[] aParamTypes=aMtd.getParameterTypes();
					
					if (aParamTypes==null || aParamTypes.length==0) {
						aFinalMtds.add(aMtd);
					}
				}	// end if(aMtd.getName)
			}
			aFinalMtdsArr=new Method[aFinalMtds.size()];
			aFinalMtdsArr=aFinalMtds.toArray(aFinalMtdsArr);
			
		} else {
			/*
			 * in case nothing to set; create an empty Method[] of size "0" 
			 */
			aFinalMtdsArr=new Method[0];
		}
		return aFinalMtdsArr;
	}
	
	/**
	 * simply a method to return the "Member" part of the given method name
	 * @param pMethodName
	 * @return
	 */
	private String getMemberNameFromAccessorMethod(final String pMethodName) {
		String aName=null;
		
		if (pMethodName!=null && pMethodName.trim().length()>0) {
			aName=pMethodName;
			aName=aName.replace("get", "");
		}
		return aName;
	}
	
	/**
	 * method to transform a JSON into an xml (the xml format is interpreted 
	 * by the 3rd party library json-xml; therefore the output might not 
	 * be desirable)
	 * @param pJObj
	 * @return
	 */
	public String transformJSONToXML(final JSONObject pJObj) {
		Transformer aTForm=null;
		InputSource aISrc=null;
		StreamResult aResult=null;
		StringReader aSReader=null;
		StringWriter aSWriter=null;
		String aXml=null;
		
		try {
			/*
			 * setup the InputSource; 
			 * PS. instead of using StreamSource (StringReader)
			 */
			aSReader=new StringReader(pJObj.toString());
			aISrc=new InputSource(aSReader);
			/*
			 * setup the Result; 
			 * PS. since a String output is expected instead of a File I/O, 
			 * StringWriter is employed
			 */
			aSWriter=new StringWriter();
			aResult=new StreamResult(aSWriter);
			/*
			 * setup of Transformer... nothing special; the only thing to note
			 * is that "INDENT" is set; or else, the output string will be 
			 * in 1 line (though after inline... still no indentation)
			 */
			aTForm=TransformerFactory.newInstance().newTransformer();
			aTForm.setOutputProperty(OutputKeys.INDENT, "yes");
			aTForm.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
			aTForm.transform(new SAXSource(new JsonXmlReader(), aISrc), aResult);
			
			aResult.getWriter().flush();
			aXml=aResult.getWriter().toString().trim();
			
		} catch (Exception e) {
			e.printStackTrace();
			
		} finally {
			if (aSReader!=null) aSReader.close();
			if (aSWriter!=null) {
				aSWriter.flush();
				try { aSWriter.close(); } catch(Exception e) {}
			}
		}
		return aXml;
	}
	
}
